package com.example.service.impl;

import com.example.annotation.RepeatSubmit;
import com.example.component.PayFactory;
import com.example.config.RabbitMQConfig;
import com.example.constant.TimeConstant;
import com.example.controller.request.ConfirmOrderRequest;
import com.example.controller.request.ProductOrderPageRequest;
import com.example.enums.BizCodeEnum;
import com.example.enums.EventMessageType;
import com.example.enums.ProductOrderPayTypeEnum;
import com.example.enums.ProductOrderStateEnum;
import com.example.exception.BizException;
import com.example.interceptor.LoginInterceptor;
import com.example.manager.ProductManager;
import com.example.manager.ProductOrderManager;
import com.example.model.EventMessage;
import com.example.model.LoginUser;
import com.example.model.ProductDO;
import com.example.model.ProductOrderDO;
import com.example.service.ProductOrderService;
import com.example.utils.CommonUtil;
import com.example.utils.JsonData;
import com.example.utils.JsonUtil;
import com.example.vo.PayInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 服务实现类
 *
 * @author yinxin
 * @since 2022-06-13
 */
@Service
@Slf4j
public class ProductOrderServiceImpl implements ProductOrderService {

    @Autowired
    private ProductOrderManager productOrderManager;

    @Autowired
    private ProductManager productManager;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private PayFactory payFactory;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Override
    public JsonData page(ProductOrderPageRequest request) {

        Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        Map<String, Object> map = productOrderManager.page(request, accountNo);
        return JsonData.buildSuccess(map);
    }

    @Override
    public JsonData getOrderState(String outTradeNo) {
        Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);
        if (productOrderDO == null) {
            return JsonData.buildSuccess(BizCodeEnum.ORDER_CONFIRM_NOT_EXIST);
        } else {
            return JsonData.buildSuccess(productOrderDO.getState());
        }
    }

    /**
     * 重复提交
     * 获取最新流量包价格
     * 订单验价
     * 如果有优惠倦或者其他抵扣
     * 验证前端显示和后台计算价格
     * 创建订单对象保存数据库
     * 创建支付
     * 发送延迟消息用于自动关毕订单
     * 创建支付信息-对接三方支付
     * 回调更新订单状态
     * 支付成功创建流量包
     *
     * @param orderRequest
     * @return
     */
    @Override
    @RepeatSubmit(lockTime = 10,limitType = RepeatSubmit.Type.PARAM)
    public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        String orderOutTradeNo = CommonUtil.getRandomCode(32);

        ProductDO productDO = productManager.findDetailById(orderRequest.getProductId());
        //验证价格(防止人为修改)
        checkPrice(productDO, orderRequest);

        //创建订单
        ProductOrderDO productOrderDO = saveProductOrder(orderRequest, loginUser, orderOutTradeNo, productDO);
        log.info("创建的订单为:{}", productOrderDO);

        //创建支付
        PayInfoVO payInfoVO = PayInfoVO.builder()
                .accountNo(loginUser.getAccountNo()).outTradeNo(orderOutTradeNo).clientType(orderRequest.getClientType())
                .payType(orderRequest.getPayType()).title(productDO.getTitle()).description("商品描述")
                .payFee(orderRequest.getPayAmount()).orderPayTimeoutMills(TimeConstant.ORDER_PAY_TIMEOUT_MILLS).build();

        //发送延迟消息（用于关闭订单）
        EventMessage eventMessage = EventMessage.builder()
                .eventMessageType(EventMessageType.PRODUCT_ORDER_NEW.name())
                .accountNo(loginUser.getAccountNo())
                .bizId(orderOutTradeNo)
                .build();
        rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(), rabbitMQConfig.getOrderCloseDelayRoutingKey()
                , eventMessage);
        //调用支付信息
        HashMap<String, Object> result = new HashMap<>();
        String codeUrl = payFactory.pay(payInfoVO);
        log.info("微信支付二维码地址为:{}", codeUrl);
        result.put("code_url", codeUrl);
        result.put("out_trade_no", orderOutTradeNo);
        return JsonData.buildSuccess(result);
    }

    /**
     * 延迟消息时间，需要比订单过期时间长一点，这样就不存在查询的时候，用户还能支付
     * <p>
     * 查询订单是否存在，如果已经支付则正常结束
     * 如果订单未支付，主动调用第三方支付平台查询订单信息
     * *确认支付，本地取消订单
     * *如果第三方平台已经支付，主动把订单状态改为已支付，造成该原因的情况可能是支付通道回调有问题，然后触发支付后的动作
     *
     * @param eventMessage
     * @return
     */
    @Override
    public Boolean closeProductOrder(EventMessage eventMessage) {
        String outTradeNo = eventMessage.getBizId();
        Long accountNo = eventMessage.getAccountNo();

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);

        if (productOrderDO == null) {
            //订单不存在
            log.warn("订单不存在");
            return true;
        }
        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) {
            //已经支付
            log.info("直接确认消息,订单已经支付了:{}", eventMessage);
            return true;
        }
        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())) {
            //未支付,需要向第三方支付平台查询状态

            PayInfoVO payInfoVO = PayInfoVO.builder()
                    .payType(productOrderDO.getPayType())
                    .outTradeNo(outTradeNo)
                    .accountNo(accountNo).build();
            //向第三方平台查询支付状态
            String payResult = payFactory.queryPayStatus(payInfoVO);
            if (StringUtils.isBlank(payResult)) {
                //如果为空，则未支付成功，本地取消订单
                productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.CANCEL.name(), ProductOrderStateEnum.NEW.name());
                log.info("未支付成功，本地取消订单:{}", eventMessage);
            } else {
                //支付成功，主动把订单状态跟新成支付
                log.warn("支付成功，但是微信回调通知失败，需要排查问题:{}", eventMessage);
                productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
                //触发支付成功后的逻辑，

                Map<String, Object> content = new HashMap<>(4);
                content.put("outTradeNo", outTradeNo);
                content.put("buyNum", productOrderDO.getBuyNum());
                content.put("accountNo", accountNo);
                content.put("product", productOrderDO.getProductSnapshot());

                //构建消息
                EventMessage payEventMessage = EventMessage.builder()
                        .bizId(outTradeNo)
                        .accountNo(accountNo)
                        .messageId(outTradeNo)
                        .content(JsonUtil.obj2Json(content))
                        .eventMessageType(EventMessageType.PRODUCT_ORDER_PAY.name())
                        .build();
                //如果key不存在，则设置成功，返回true
                Boolean flag = redisTemplate.opsForValue().setIfAbsent(outTradeNo, "OK", 3, TimeUnit.DAYS);
                if (flag) {
                    rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(),
                            rabbitMQConfig.getOrderUpdateTrafficRoutingKey(), payEventMessage);
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 微信支付成功后的回调通知
     *
     * @param payType
     * @param paramsMap
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)//什么异常导致事务回滚,传播属性
    public JsonData processOrderCallbackMsg(ProductOrderPayTypeEnum payType, Map<String, String> paramsMap) {
        String outTradeNo = paramsMap.get("out_trade_no");//订单号
        String tradeState = paramsMap.get("trade_state");//交易状态
        Long accountNo = Long.valueOf(paramsMap.get("account_no"));//用户账号

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);

        Map<String, Object> content = new HashMap<>(4);
        content.put("outTradeNo", outTradeNo);
        content.put("buyNum", productOrderDO.getBuyNum());
        content.put("accountNo", productOrderDO.getAccountNo());
        content.put("product", productOrderDO.getProductSnapshot());

        //构建消息
        EventMessage eventMessage = EventMessage.builder()
                .bizId(outTradeNo)
                .accountNo(accountNo)
                .messageId(outTradeNo)
                .content(JsonUtil.obj2Json(content))
                .eventMessageType(EventMessageType.PRODUCT_ORDER_PAY.name())
                .build();
        if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.ALI_PAY.name())) {
            //支付宝支付逻辑
        } else if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT_PAY.name())) {
            //微信支付
            if ("SUCCESS".equalsIgnoreCase(tradeState)) {
                //如果key不存在，则设置成功，返回true(防止微信重复推送消息)
                Boolean absent = redisTemplate.opsForValue().setIfAbsent(outTradeNo, "OK", 3, TimeUnit.DAYS);
                if (absent) {
                    rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange()
                            , rabbitMQConfig.getOrderUpdateTrafficRoutingKey(), eventMessage);
                    return JsonData.buildSuccess();
                }

            }
        }

        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
    }

    /**
     * 处理订单相关消息
     *
     * @param eventMessage
     */
    @Override
    public void handleProductOrderMessage(EventMessage eventMessage) {
        String messageType = eventMessage.getEventMessageType();
        try {
            if (EventMessageType.PRODUCT_ORDER_NEW.name().equalsIgnoreCase(messageType)) {
                //关闭订单
                this.closeProductOrder(eventMessage);
            } else if (EventMessageType.PRODUCT_ORDER_PAY.name().equalsIgnoreCase(messageType)) {
                //订单已经支付，更新订单状态
                String outTradeNo = eventMessage.getBizId();
                Long accountNo = eventMessage.getAccountNo();
                int rows = productOrderManager.updateOrderPayState(outTradeNo, accountNo
                        , ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
                log.info("订单更新成功:{};eventMessage={}", rows, eventMessage);
            }


        } catch (Exception e) {
            log.error("消费者消费失败:{}", e.getMessage());
            throw new BizException(BizCodeEnum.MQ_CONSUME_EXCEPTION);
        }

    }

    /**
     * 存订单信息，创建订单
     *
     * @param orderRequest
     * @param loginUser
     * @param orderOutTradeNo
     * @param productDO
     * @return
     */
    private ProductOrderDO saveProductOrder(ConfirmOrderRequest orderRequest, LoginUser loginUser, String orderOutTradeNo, ProductDO productDO) {
        ProductOrderDO productOrderDO = new ProductOrderDO();
        //设置用户信息
        productOrderDO.setAccountNo(loginUser.getAccountNo());
        productOrderDO.setNickname(loginUser.getUsername());

        //设置商品信息
        productOrderDO.setProductId(productDO.getId());
        productOrderDO.setProductTitle(productDO.getTitle());
        productOrderDO.setProductSnapshot(JsonUtil.obj2Json(productDO));
        productOrderDO.setProductAmount(productDO.getAmount());

        //设置订单信息
        productOrderDO.setBuyNum(orderRequest.getBuyNum());
        productOrderDO.setOutTradeNo(orderOutTradeNo);
        productOrderDO.setCreateTime(new Date());
        productOrderDO.setDel(0);

        //发票信息
        productOrderDO.setBillType(orderRequest.getBillType());
        productOrderDO.setBillHeader(orderRequest.getBillHeader());
        productOrderDO.setBillReceiverPhone(orderRequest.getBillReceiverPhone());
        productOrderDO.setBillReceiverEmail(orderRequest.getBillReceiverEmail());
        productOrderDO.setBillContent(orderRequest.getBillContent());

        //实际支付总价
        productOrderDO.setPayAmount(orderRequest.getPayAmount());
        //总价没使用优惠卷
        productOrderDO.setTotalAmount(orderRequest.getTotalAmount());
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name());

        productOrderManager.add(productOrderDO);

        return productOrderDO;

    }

    /**
     * 验算付款价格是否正确
     *
     * @param productDO
     * @param orderRequest
     */
    private void checkPrice(ProductDO productDO, ConfirmOrderRequest orderRequest) {

        //后台计算价格
        BigDecimal bizTotal = BigDecimal.valueOf(orderRequest.getBuyNum()).multiply(productDO.getAmount());

        //前端传递总价和后端计算总价格是否一致,如果有优惠卷,也可以在这边计算
        if (bizTotal.compareTo(orderRequest.getPayAmount()) != 0) {
            log.error("验证价格失败{}", orderRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }
    }
}
